[Day12] 啊你說這個不能吃是嗎?
哈囉!今天要跟大家介紹的是模組(Module)。
在 Ruby 的世界裡,模組是一個特殊又好用的東西,它就跟大名鼎鼎「惡魔果實」一樣,只要吃下去就能獲得那顆果實的特殊能力(完全是個不用靠爸就能成為主角的神奇道具啊!)
在理解模組時,可以想成是它加裝了一個東西原本沒有的功能,大概就像這樣:
這真是太神奇了傑克!
有了模組,我們就不需要使用昨天介紹到的類別繼承,那麼,要如何做到這件事呢?
include
請先詳閱以下使用說明書:
先定義一個模組 Floatable
:
模組的命名規則就和類別一樣,也必須是大字英文字母開頭的常數。
module Floatable
def float
puts "浮起來了!"
end
end
再定義一個 Bicycle
的類別:
class Bicycle
end
產生一個 your_bike
實體,然後使用 float
方法試試看:
your_bike = Bicycle.new
your_bike.float
=> NoMethodError (undefined method `float' for #<Bicycle:0x00007fd90901ba28>)
居然失敗了!為什麼?
因為沒有在腳踏車上裝上可以浮起來的氣墊啊!怎麼可能會成功!
include
來裝模組!---在 Bicycle
類別裝上剛剛做好的 Floatable
模組吧!
class Bicycle
include Floatable # 引入模組
end
重新產生一個 my_bike
實體,然後使用 float
方法:
my_bike = Bicycle.new
my_bike.float
# 印出
浮起來了!
嘿嘿~現在我的腳踏車可以浮在水面上了!
extend
接著,我們再來看看更神奇的 extend
:
假如你今天有一顆引擎的模組,然後把它裝在腳踏車上,腳踏車從此以後就可以用引擎運轉前進了!不過,如果想把腳踏車改造成氮氣噴射的功能,變成阿姆斯特朗旋風噴射阿姆斯特朗砲
的話呢?
這時候就會用上讓模組擴充模組的 extend
了!
廢話說完了,接著來看 code:
module Engine
def run
puts "全速啟動!"
end
end
module Acceleration
def accelerate
puts "氮氣噴射!!!"
end
end
class Bike
include Engine
extend Acceleration
end
max = Bike.new
max.run
# 印出
全速啟動!
Bike.accelerate
# 印出
氮氣噴射!!!
看出來了嗎? 原本的 Engine
模組在被 include
後,只是讓腳踏車可以用引擎運轉,但 Acceleration
模組被 extend
後則徹底改造了腳踏車這個類別,讓它完全升級為一種不同的交通工具了(誤)
但如果讓 max
呼叫 accelerate
方法或是讓 Bike
呼叫 run
:
max.accelerate
# 印出
NoMethodError (undefined method `accelerate' for #<Bike:0x00007fec3f2aaf70>)
Bike.run
# 印出
NoMethodError (undefined method `run' for Bike:Class)
就都找不到方法了,所以...
說人話版本 =>
Bike.accelerate # 類別方法 / 用 extend 引入
Bike.new.run # 實體方法 / 用 include 引入
明天會再介紹什麼是類別方法和實體方法!(挖坑)
是的,他們很像。
譬如名字都是大寫字母開頭、裡面會放一些方法。在專案裡,如果只看到一個大寫開頭的常數,並沒有辦法確定現在看到的是模組還是類別!不過,它們還是有一些明顯的差異。
舉個例子,可能很多人在純真的童年時會夢想像鳥類一樣在天上飛,在20世紀初,萊特兄弟發明了可以載人的飛機,但直到今天,人們仍然不算是學會了飛行。
如果真的要讓人類可以飛,該怎麼做?
首先,鳥類這個類別應該會有一個 fly
的方法,
class Bird
def fly
# 可以飛行的秘密
end
end
然後繼承它?class Human < Birdend
等等!我們的祖先又不是鳥類,所以不應該使用類別繼承吧!
那掛載一個會可以讓人飛行的模組?
module Flyable
def fly
# 可以飛行的秘密
end
end
class Human
intend Flayable
end
我們終於成功裝上了 Flyable
!從此以後,人類就可以飛了!
Class
和 Module
的方法數量瞎扯了一堆,接著我們直接進程式裡看看:
# 查詢 Class 和 Module 的方法和數量
p Module.instance_methods.count
109
p Class.instance_methods.count
112
instance_methods.count
可以幫我們計算一個類別能使用的方法有多少,可以發現雖然兩者的方法都很多,但 Class
比 Module
硬是多出了 3 個方法!
來看看多出的是哪幾個:
p Class.instance_methods(false)
[:new, :allocate, :superclass]
由於 Module
少了 :new
, :allocate
, :superclass
這些方法 ,所以:
new
一個新的實體。無法繼承
別的模組。m = Module.new
m
=> #<Module:0x00007f97268b6310>
m.new
=> NoMethodError (undefined method `new' for #<Module:0x00007f97268b6310>)
::
namespace
?這又是什麼?想像一下,我的車有一顆引擎,你的車也有一顆引擎,但你開的是 BMW 旗艦 V12 雙門跑車,我開的是 Toyota Camry (Camry 錯了嗎?),那這兩輛車會是同樣的車嗎?很明顯不是!(怒)
那要怎麼解決這種問題呢,簡單來說,namespace
就是為了避免叫到同名類別,導致程式無法判讀的問題。我們在呼叫類別時,可以連名帶姓地呼叫:
譬如專案裡常常會看到的:
# 類別(模組)::類別
class User < ActiveRecord::Base
end
class A
module B
end
end
如此一來,我們就能很清楚地知道 User
類別是繼承自 Base
類別,而這個 Base
類別是屬於 ActiveRecord
這個模組(或類別)裡的,其他地方如果有叫 Base
的類別都不是!
有興趣的朋友可以看看龍哥的這篇大作:
Ruby 語法放大鏡之「有時候會看到有兩個冒號寫法是什麼意思?」
說了這麼多,今天就先到這邊囉!
相信大家應該都認識模組了!明天見囉!
旗木卡卡西:是不是忘了介紹我?寫輪眼才是最強的模組啊!